using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
using Oplaco.Samples.Repository;
using Oplaco.Samples.Models;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace WorkerRoleStorage
{
    /// <summary>
    /// Storage worker role sample
    /// </summary>
    public class WorkerRole : RoleEntryPoint
    {
        #region fields
        private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);
        private CloudStorageAccount storageAccount;
        private CloudBlobContainer imagesBlobContainer;
        private CloudQueue imagesQueue;
        private ArticleRepository repos = new ArticleRepository();
        #endregion


        /// <summary>
        /// To make the worker role more scalable, implement multi-threaded and asynchronous code
        /// </summary>
        /// <see cref="http://azure.microsoft.com/nl-nl/documentation/articles/cloud-services-dotnet-get-started/"/>
        /// <seealse cref="http://msdn.microsoft.com/en-us/library/ck8bc5c6.aspx"/>
        /// <seealso cref="http://www.asp.net/aspnet/overview/developing-apps-with-windows-azure/building-real-world-cloud-apps-with-windows-azure/web-development-best-practices#async"/>
        /// <remarks>
        /// A production app could be more efficient and scalable and conserve
        /// on transaction costs by using the GetMessages method to get
        /// multiple queue messages at a time.
        /// </remarks>
        /// <seealso cref="http://azure.microsoft.com/en-us/documentation/articles/cloud-services-dotnet-multi-tier-app-storage-5-worker-role-b/#addcode"/>
        public override void Run()
        {
            Trace.TraceInformation("WorkerRoleStorage is running");
            CloudQueueMessage msg = null;
            try
            {
                this.RunAsync(this.cancellationTokenSource.Token).Wait();
                msg = this.imagesQueue.GetMessage();
                if (msg != null)
                {
                    ProcessQueueMessage(msg);
                }
                else
                {
                    System.Threading.Thread.Sleep(1000);
                }
            }
            catch (StorageException e)
            {
                if (msg != null && msg.DequeueCount > 5)
                {
                    this.imagesQueue.DeleteMessage(msg);
                    Trace.TraceError("Deleting poison queue item: '{0}'", msg.AsString);
                }
                Trace.TraceError("Exception in ContosoAdsWorker: '{0}'", e.Message);
                System.Threading.Thread.Sleep(5000);
            }
            finally
            {
                this.runCompleteEvent.Set();
            }
        }

        /// <summary>
        /// Initializes the storage account, prepares the containers
        /// </summary>
        /// <returns>Bool</returns>
        /// <see cref="http://azure.microsoft.com/en-us/documentation/articles/cloud-services-dotnet-multi-tier-app-storage-3-web-role/#restarts"/>
        public override bool OnStart()
        {
            // Set the maximum number of concurrent connections
            ServicePointManager.DefaultConnectionLimit = 12;

            Trace.TraceInformation("WorkerRoleStorage has been started");
            this.storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnnectionString"));
            var blobClient = storageAccount.CreateCloudBlobClient();
            imagesBlobContainer = blobClient.GetContainerReference("images");

            if (imagesBlobContainer.CreateIfNotExists())
            {
                // Enable public access on the newly created "images" container.
                imagesBlobContainer.SetPermissions(new BlobContainerPermissions {
                        PublicAccess = BlobContainerPublicAccessType.Blob
                    });
            }

            Trace.TraceInformation("Creating images queue");
            CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
            imagesQueue = queueClient.GetQueueReference("images");
            imagesQueue.CreateIfNotExists();

            Trace.TraceInformation("Storage initialized");
            return base.OnStart(); 
        }

        /// <summary>
        /// 
        /// </summary>
        /// <see cref="http://azure.microsoft.com/en-us/documentation/articles/cloud-services-dotnet-multi-tier-app-storage-3-web-role/#restarts"/>
        public override void OnStop()
        {
            Trace.TraceInformation("WorkerRoleStorage is stopping");

            this.cancellationTokenSource.Cancel();
            this.runCompleteEvent.WaitOne();

            base.OnStop();

            Trace.TraceInformation("WorkerRoleStorage has stopped");
        }

        private async Task RunAsync(CancellationToken cancellationToken)
        {
            // TODO: Replace the following with your own logic.
            while (!cancellationToken.IsCancellationRequested)
            {
                Trace.TraceInformation("Working");
                await Task.Delay(1000);
            }
        }

        private void ProcessQueueMessage(CloudQueueMessage msg)
        {
            Trace.TraceInformation("Processing queue message {0}", msg);

            // Queue message contains AdId.
            var aId = int.Parse(msg.AsString);
            FileItem img = repos.GetImageById(aId);
            if (img == null)
            {
                throw new Exception(String.Format("AId {0} not found, can't create thumbnail", aId.ToString()));
            }

            CloudBlockBlob inputBlob = this.imagesBlobContainer.GetBlockBlobReference(img.Url);

            string thumbnailName = Path.GetFileNameWithoutExtension(inputBlob.Name) + "thumb.jpg";
            CloudBlockBlob outputBlob = this.imagesBlobContainer.GetBlockBlobReference(thumbnailName);

            using (Stream input = inputBlob.OpenRead())
            using (Stream output = outputBlob.OpenWrite())
            {
                ConvertImageToThumbnailJPG(input, output);
                outputBlob.Properties.ContentType = "image/jpeg";
            }
            Trace.TraceInformation("Generated thumbnail in blob {0}", thumbnailName);

            FileItem thumbnail = new FileItem
            {
                Url = outputBlob.Uri.ToString(),
                Id = Guid.NewGuid(),
                FileType = MultimediaTypeEnum.Thumbnail,
                Alias = "thumb"+ img.Alias,
                LastModified = DateTime.Now,
                ArticleId = img.ArticleId,
                ParentId = img.Id,
            };
            repos.AddImage(thumbnail);
            Trace.TraceInformation("Created thumbnail URL in database: {0}", img.Url);

            // Remove message from queue.
            this.imagesQueue.DeleteMessage(msg);
        }

        public void ConvertImageToThumbnailJPG(Stream input, Stream output)
        {
            int thumbnailsize = 80;
            int width;
            int height;
            var originalImage = new Bitmap(input);

            if (originalImage.Width > originalImage.Height)
            {
                width = thumbnailsize;
                height = thumbnailsize * originalImage.Height / originalImage.Width;
            }
            else
            {
                height = thumbnailsize;
                width = thumbnailsize * originalImage.Width / originalImage.Height;
            }

            Bitmap thumbnailImage = null;
            try
            {
                thumbnailImage = new Bitmap(width, height);

                using (Graphics graphics = Graphics.FromImage(thumbnailImage))
                {
                    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    graphics.SmoothingMode = SmoothingMode.AntiAlias;
                    graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    graphics.DrawImage(originalImage, 0, 0, width, height);
                }

                thumbnailImage.Save(output, ImageFormat.Jpeg);
            }
            finally
            {
                if (thumbnailImage != null)
                {
                    thumbnailImage.Dispose();
                }
            }
        }
    }
}
